#include "ServerGamePlayState.h"
#include "WellModel.h"
#include "Server.h"
#include "Configuration.h"
#include "ScoreModel.h"
#include "ModelComposite.h"

ServerGamePlayState::ServerGamePlayState(ClientPtr client, ServerPtr server, PlayersPtr players, unsigned int widthOfWell)
        : GamePlayState(client, widthOfWell), server(server), players(players), msSinceLastMoveDown(0),
                msSinceLastGravitationSpeedChange(0), msGravitationSpeed(1000)
{
    rootModel = new ModelComposite();
    wellModel = new WellModel(players, widthOfWell);
    scoreModel = new ScoreModel(players);
    rootModel->addChild(wellModel);
    rootModel->addChild(scoreModel);
}

ServerGamePlayState::~ServerGamePlayState()
{
    delete rootModel;
}

void ServerGamePlayState::updateGravitationSpeed(unsigned long msSinceLastLoop)
{
    msSinceLastGravitationSpeedChange += msSinceLastLoop;
    if (msSinceLastGravitationSpeedChange >= Configuration::MS_BETWEEN_GRAVITATION_SPEED_CHANGES)
    {
        msSinceLastGravitationSpeedChange -= Configuration::MS_BETWEEN_GRAVITATION_SPEED_CHANGES;
        msGravitationSpeed = msGravitationSpeed / Configuration::GRAVITATION_SPEED_CHANGE_FACTOR;
    }
}

void ServerGamePlayState::movePiecesDown(unsigned long msSinceLastLoop)
{
    msSinceLastMoveDown += msSinceLastLoop;
    if (msSinceLastMoveDown >= msGravitationSpeed)
    {
        msSinceLastMoveDown -= msGravitationSpeed;
        wellModel->movePiecesDown();
    }
}

GameState* ServerGamePlayState::goToGameOverState(const std::string& winner)
{
    server->quit();
    return GamePlayState::goToGameOverState(winner);
}

GameState* ServerGamePlayState::leaveGame()
{
    server->quit();
    return GamePlayState::leaveGame();
}

void ServerGamePlayState::updateClients()
{
    NetworkEvents events;
    rootModel->collectChanges(events);
    server->sendToClients(events);
}

void ServerGamePlayState::init()
{
    wellModel->init();
    scoreModel->init();
    GamePlayState::init();
}

GameState* ServerGamePlayState::handleEvent(const Event& event)
{
    rootModel->traverse(event);
    return GamePlayState::handleEvent(event);
}

GameState* ServerGamePlayState::update(unsigned long msSinceLastLoop)
{
    updateGravitationSpeed(msSinceLastLoop);
    movePiecesDown(msSinceLastLoop);
    updateClients();
    return this;
}

